// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UniverseExpoCell.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Usings

    using System;
    using System.Collections.Generic;
    using System.Reflection;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;

    #endregion

    /// <summary>
    /// The Universe expo cell.
    /// </summary>
    public class UniverseExpoCell : IUniverseExpoCell
    {
        #region Constants and Fields

        /// <summary>
        /// The identity.
        /// </summary>
        private static readonly Identity Identity = Identity.Create(MethodBase.GetCurrentMethod());

        /// <summary>
        /// Collection of InstrumentListExpoCells
        /// </summary>
        private readonly IList<InstrumentListExpoCell> cells = new List<InstrumentListExpoCell>();

        /// <summary>
        /// fees
        /// </summary>
        private double fees;

        /// <summary>
        /// Long side nominal
        /// </summary>
        private double nominalLong;

        /// <summary>
        /// Short side nominal
        /// </summary>
        private double nominalShort;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UniverseExpoCell"/> class. 
        /// Create Universe expo cell
        /// </summary>
        /// <param name="type">
        /// </param>
        public UniverseExpoCell(EExpoCellType type)
        {
            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("Creating new UniverseExpoCell ({0})", type));

            // Expo Type
            this.ExpoCellType = type;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Type of the expo
        /// </summary>
        public EExpoCellType ExpoCellType { get; private set; }

        /// <summary>
        /// Gets Fees.
        /// </summary>
        public double Fees
        {
            get
            {
                this.Reset();
                return this.fees;
            }

            private set
            {
                if (value >= 0.0)
                {
                    this.fees = value;
                }
            }
        }

        /// <summary>
        /// Gets InventoryEventDelegate.
        /// </summary>
        public InventoryEventDelegate InventoryEventDelegate
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Check if the vales are changed
        /// </summary>
        public bool IsChanged { get; protected set; }

        /// <summary>
        /// TRUE if NominalLong > NominalShort
        /// </summary>
        public bool IsLong
        {
            get
            {
                this.Reset();
                return this.NominalLong > this.NominalShort;
            }
        }

        /// <summary>
        /// TRUE if NominalShort > NominalLong  
        /// </summary>
        public bool IsShort
        {
            get
            {
                this.Reset();
                return this.NominalLong < this.NominalShort;
            }
        }

        /// <summary>
        /// Check if updated
        /// </summary>
        public bool IsUpdated { get; protected set; }

        /// <summary>
        /// Check if is updating values
        /// </summary>
        public bool IsUpdating { get; protected set; }

        /// <summary>
        /// LastUpdate Time
        /// </summary>
        public DateTime LastUpdate { get; protected set; }

        /// <summary>
        /// Gets NetPL.
        /// </summary>
        public double NetPL
        {
            get
            {
                return this.PL - this.Fees;
            }
        }

        /// <summary>
        /// Return the market expo : Long - Short
        /// </summary>
        public double NominalExpo
        {
            get
            {
                return this.NominalLong - this.NominalShort;
            }
        }

        /// <summary>
        /// Gets NominalLong.
        /// </summary>
        public double NominalLong
        {
            get
            {
                this.Reset();
                return this.nominalLong;
            }

            private set
            {
                if (value >= 0.0)
                {
                    this.nominalLong = value;
                }
            }
        }

        /// <summary>
        /// Gets NominalShort.
        /// </summary>
        public double NominalShort
        {
            get
            {
                this.Reset();
                return this.nominalShort;
            }

            private set
            {
                if (value >= 0.0)
                {
                    this.nominalShort = value;
                }
            }
        }

        /// <summary>
        /// Gets OrderEventDelegate.
        /// </summary>
        public OrderEventDelegate OrderEventDelegate
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets PL.
        /// </summary>
        public double PL
        {
            get
            {
                this.Reset();
                return this.Realized + this.UnRealized;
            }
        }

        /// <summary>
        /// Gets Realized.
        /// </summary>
        public double Realized { get; private set; }

        /// <summary>
        /// Gets RefPrice = 0.0.
        /// </summary>
        public double RefPrice
        {
            get
            {
                return 0.0;
            }
        }

        /// <summary>
        /// Gets UnRealized.
        /// </summary>
        public double UnRealized { get; private set; }

        #endregion

        #region Indexers

        /// <summary>
        /// Get InstrumentListExpoCell using index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public InstrumentListExpoCell this[int index]
        {
            get
            {
                return this.cells[index];
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Set members to default value (null)
        /// </summary>
        public void Init()
        {
            this.NominalLong = 0.0;
            this.NominalShort = 0.0;
            this.Fees = 0.0;
        }

        #endregion

        #region Implemented Interfaces

        #region IUniverseExpoCell

        /// <summary>
        /// Add cell
        /// </summary>
        /// <param name="cell">
        /// </param>
        /// <returns>
        /// The add.
        /// </returns>
        public bool Add(InstrumentListExpoCell cell)
        {
            if (cell == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Add : Cannot add InstrumentListExpoCell {0}, Invalid cell", this.ExpoCellType));
                return false;
            }

            if (cell.ExpoType != this.ExpoCellType)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () => string.Format("Add : Cannot add InstrumentListExpoCell, Invalid cell type {0} != {1}", cell.ExpoType, this.ExpoCellType));
                return false;
            }

            cell.OrderEventCB += this.OnOrderEvent;
            this.cells.Add(cell);

            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("Add : Added InstrumentListExpoCell {0}", cell.ExpoType));

            return true;
        }

        /// <summary>
        /// Remove cell
        /// </summary>
        /// <param name="cell">
        /// </param>
        /// <returns>
        /// The remove.
        /// </returns>
        public bool Remove(InstrumentListExpoCell cell)
        {
            if (cell == null)
            {
                Logging.Log(
                    Identity, ELogLevel.Error, () => string.Format("Remove : Cannot remove InstrumentListExpoCell {0}, Invalid cell", this.ExpoCellType));
                return false;
            }

            cell.OrderEventCB -= this.OnOrderEvent;
            this.cells.Remove(cell);

            Logging.Log(Identity, ELogLevel.Debug, () => string.Format("Remove : removed InstrumentListExpoCell {0}", cell.ExpoType));

            return true;
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// OnOrderEvent
        /// </summary>
        /// <param name="sender">
        /// </param>
        /// <param name="args">
        /// </param>
        private void OnOrderEvent(object sender, OrderEventArgs args)
        {
            if (sender == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Notify : Cannot notify universe expo cell {0}, Invalid sender", this.ExpoCellType));
                return;
            }

            if (sender as InstrumentListExpoCell == null)
            {
                Logging.Log(Identity, ELogLevel.Error, () => string.Format("Notify : Cannot notify universe expo cell {0}, Invalid sender", this.ExpoCellType));
                return;
            }

            if ((sender as InstrumentListExpoCell).ExpoType != this.ExpoCellType)
            {
                Logging.Log(
                    Identity, 
                    ELogLevel.Error, 
                    () =>
                    string.Format(
                        "Notify : Cannot notify universe expo cell {0}, Invalid Expo cell type of the sender {1}", 
                        this.ExpoCellType, 
                        ((InstrumentListExpoCell)sender).ExpoType));
                return;
            }

            // Update values
            this.IsChanged = true;
            this.LastUpdate = Timings.Now;

            // Logging
            Logging.Log(
                Identity, 
                ELogLevel.Debug, 
                () =>
                string.Format(
                    "OnOrderEvent : received event {0} : {1} - {2}", 
                    this.LastUpdate, 
                    ((InstrumentListExpoCell)sender).Instrument.InstrumentStaticData.Code, 
                    this.ExpoCellType));
        }

        /// <summary>
        /// Reset : update values
        /// </summary>
        private void Reset()
        {
            // if (!this.IsChanged)
            // {
            // return;
            // }

            // if (this.IsUpdating)
            // {
            // return;
            // }

            // if (this.IsUpdated)
            // {
            // return;
            // }

            // this.IsUpdating = true;
            // this.IsUpdated = false;
            var l = 0.0;
            var s = 0.0;
            var f = 0.0;
            var r = 0.0;
            var u = 0.0;

            foreach (var cell in this.cells)
            {
                if (cell != null)
                {
                    l += cell.NominalLong * cell.FactorPrice;
                    s += cell.NominalShort * cell.FactorPrice;
                    f += cell.FeesTracker != null ? cell.FeesTracker.Fees * cell.FactorPrice : 0.0;
                    r += cell.Realized * cell.FactorPrice;
                    u += cell.UnRealized * cell.FactorPrice;
                }
            }

            this.NominalLong = l;
            this.NominalShort = s;
            this.Fees = f;
            this.Realized = r;
            this.UnRealized = u;

            // this.IsChanged = false;
            // this.IsUpdated = true;
            // this.IsUpdating = false;
        }

        #endregion
    }
}